// 平行光
// 照射所有方向的光源
import { 
  AmbientLight, 
  AxesHelper, 
  BoxGeometry, 
  CameraHelper, 
  Color, 
  Mesh, MeshPhongMaterial, MeshLambertMaterial, 
  Object3D, 
  PerspectiveCamera, PlaneGeometry, Scene, 
  SphereGeometry, 
  Vector3, 
  WebGLRenderer, 
  DirectionalLight,
  MeshBasicMaterial
} from "three"
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls'
import Stats from 'stats.js'
import dat from 'dat.gui'

const scene = new Scene()
const camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000)
const renderer = new WebGLRenderer()
renderer.setClearColor(0x000000)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true

document.body.appendChild(renderer.domElement)

// 创建一个平面
const planeGeometry = new PlaneGeometry(200, 100, 20, 20)
const planeMaterial = new MeshPhongMaterial({ color: 0xffffff })
const plane = new Mesh(planeGeometry, planeMaterial)
// 接收阴影
plane.receiveShadow = true

plane.rotation.x = -0.5 * Math.PI
// plane.position.set(0, 0, 0)
scene.add(plane)

const geometry = new BoxGeometry(4, 4, 4)
const material = new MeshLambertMaterial({ color: 0xff3333 })
const cube = new Mesh(geometry, material)

cube.position.set(-10, 2, 0)
// 正方体投射阴影
cube.castShadow = true
scene.add( cube )

// 生成一个球
let sphereLight0 = new SphereGeometry(4, 20, 20)
let sphereLightMaterial0 = new MeshLambertMaterial({color: 0x7777ff})
let sphereLightMesh0 = new Mesh(sphereLight0, sphereLightMaterial0)
sphereLightMesh0.position.set(20, 4, 2)
sphereLightMesh0.castShadow = true
scene.add(sphereLightMesh0)

// 添加坐标系，方便理解
const axes = new AxesHelper(15)
scene.add(axes)

// 设置相机位置
camera.position.set(-80, 80, 100)
camera.lookAt(new Vector3(5, 0, 0))

// 添加环境光
let ambientColor = '#1c1c1c'
const ambientLight = new AmbientLight(ambientColor)
scene.add(ambientLight)

// 添加一个目标与光源
let target = new Object3D()
target.position.copy(new Vector3(5, 0, 0))

// 生成另一个平行光
let pointColor = '#ff5808'
let directionalLight = new DirectionalLight(pointColor)
directionalLight.position.set(0, 60, 0)
directionalLight.castShadow = true
directionalLight.shadow.camera.near = 2
directionalLight.shadow.camera.far = 80
directionalLight.shadow.camera.left = -30
directionalLight.shadow.camera.right = 30
directionalLight.shadow.camera.top = 30
directionalLight.shadow.camera.bottom = -30
directionalLight.shadow.mapSize.width = 1024
directionalLight.shadow.mapSize.height = 1024
directionalLight.intensity = 0.5
directionalLight.target = plane
scene.add(directionalLight)

let shadowCamera = new CameraHelper(directionalLight.shadow.camera)
// scene.add(shadowCamera)

let sphereLight = new SphereGeometry(0.2)
let sphereLightMaterial = new MeshBasicMaterial({ color: 0xac6c25})
let sphereLightMesh = new Mesh(sphereLight, sphereLightMaterial)
sphereLightMesh.castShadow = true
sphereLightMesh.position.set(3, 20, 3)
scene.add(sphereLightMesh)

// 添加 FPS 监控
let stats: Stats = new Stats()
stats.showPanel(0)
document.body.appendChild(stats.dom)

const controls = setupControls()
// 引入轨迹球控制器，添加鼠标控制功能
const trackballControls = new TrackballControls(camera, renderer.domElement)

let step = 0

function render() {
  trackballControls.update()
  stats.update()

  step += controls.bouncingSpeed
  sphereLightMesh.position.z = -8
  sphereLightMesh.position.y = +(27 * Math.sin(step / 3))
  sphereLightMesh.position.x = 10 +(26 * Math.cos(step / 3))

  directionalLight.position.copy(sphereLightMesh.position)

  requestAnimationFrame(render)
  renderer.render(scene, camera)
}
render()

function setupControls() {
  // 控制器
  let controls = new function () {
    this.rotationSpeed = 0.03
    this.bouncingSpeed = 0.03
    this.ambientColor = ambientLight.color.getStyle()
    this.pointColor = directionalLight.color.getStyle()
    this.intensity = 0.5
    this.debug = false
    this.castShadow = true
    this.target = 'Plane'
  }

  const gui = new dat.GUI()
  // 颜色选择功能
  gui.addColor(controls, 'ambientColor')
    .onChange(e => {
      ambientLight.color = new Color(e)
    })

  gui.addColor(controls, 'pointColor').onChange(e => {
    directionalLight.color = new Color(e)
  })

  // 强度
  gui.add(controls, 'intensity', 0, 5).onChange(e => {
    directionalLight.intensity = e
  })

  gui.add(controls, 'debug').onChange(e => {
    if (e) {
      scene.add(shadowCamera)
    } else {
      scene.remove(shadowCamera)
    }
  })

  gui.add(controls, 'castShadow').onChange(e => {
    directionalLight.castShadow = e
  })

  gui.add(controls, 'target', ['Plane', 'Cube', 'Sphere']).onChange(e => {
    switch (e) {
      case 'Plane':
        directionalLight.target = plane
        break
      case 'Cube':
        directionalLight.target = cube
        break
      case 'Sphere':
        directionalLight.target = sphereLightMesh0
    }
  })

  return controls
}